/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.util.lang;

import java.lang.constant.ConstantDesc;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import lombok.experimental.UtilityClass;

/**
 * BeanUtils
 *
 * @author zeng
 */
@UtilityClass
public class BeanUtils {
  /** 字段过滤器接口，用于根据字段名判断是否保留该字段。 */
  public interface FieldFilter extends Predicate<String> {}

  /**
   * 转义bean中所有属性为字符串的
   *
   * @param bean {@link Object}
   */
  public static void beanAttributeValueEscapeXml(Object bean) {
    try {
      if (bean != null) {
        // 获取所有的字段包括public,private,protected,private
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field f : fields) {
          if ("java.lang.String".equals(f.getType().getName())) {
            // 获取字段名
            String key = f.getName();
            Object value = getFieldValue(bean, key);

            if (value == null) {
              continue;
            }
            setFieldValue(bean, key, escapeXml(value.toString()));
          }
        }
      }
    } catch (Exception e) {
      throw ExceptionUtils.mpe(e);
    }
  }

  /**
   * 转义xml中的特殊字符
   *
   * @param input {@link String}
   * @return {@link String}
   */
  public static String escapeXml(String input) {
    if (input == null) {
      return "";
    }
    return input
        .replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace("\"", "&quot;")
        .replace("'", "&apos;");
  }

  /**
   * bean 中所有属性为字符串的进行\n\t\s处理
   *
   * @param bean {@link Object}
   */
  public static void beanAttributeValueReplaceBlank(Object bean) {
    try {
      if (bean != null) {
        // 获取所有的字段包括public,private,protected,private
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field f : fields) {
          if ("java.lang.String".equals(f.getType().getName())) {
            // 获取字段名
            String key = f.getName();
            Object value = getFieldValue(bean, key);
            if (value == null) {
              continue;
            }
            setFieldValue(bean, key, StringUtils.replaceBlank(value.toString()));
          }
        }
      }
    } catch (Exception e) {
      throw ExceptionUtils.mpe(e);
    }
  }

  /**
   * 去掉bean中所有属性为字符串的前后空格
   *
   * @param bean {@link Object}
   */
  public static void beanAttributeValueTrim(Object bean) {
    try {
      if (bean != null) {
        // 获取所有的字段包括public,private,protected,private
        Field[] fields = bean.getClass().getDeclaredFields();
        for (Field f : fields) {
          if ("java.lang.String".equals(f.getType().getName())) {
            // 获取字段名
            String key = f.getName();
            Object value = getFieldValue(bean, key);

            if (value == null) {
              continue;
            }
            setFieldValue(bean, key, value.toString().trim());
          }
        }
      }
    } catch (Exception e) {
      throw ExceptionUtils.mpe(e);
    }
  }

  /**
   * 利用反射通过get方法获取bean中字段fieldName的值
   *
   * @param bean {@link Object}
   * @param fieldName {@link String}
   * @return {@link Object}
   * @throws Exception Exception
   */
  private static Object getFieldValue(Object bean, String fieldName) throws Exception {
    String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);

    Object object;
    Method method;

    @SuppressWarnings("rawtypes")
    Class[] classArr = new Class[0];
    method = bean.getClass().getMethod(methodName, classArr);
    object = method.invoke(bean);

    return object;
  }

  /**
   * 利用发射调用bean.set方法将value设置到字段
   *
   * @param bean {@link Object}
   * @param fieldName {@link String}
   * @param value {@link Object}
   * @throws Exception Exception
   */
  private static void setFieldValue(Object bean, String fieldName, Object value) throws Exception {
    String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
    // 利用发射调用bean.set方法将value设置到字段
    Class<?>[] classArr = new Class[1];
    classArr[0] = "java.lang.String".getClass();
    Method method = bean.getClass().getMethod(methodName, classArr);
    method.invoke(bean, value);
  }

  /**
   * 将任意 Java 对象转换为 Map 《String, Object》 包含所有声明字段（包括继承字段）
   *
   * @param bean bean
   * @return map
   */
  public static Map<String, Object> toMap(Object bean) {
    return toMap(bean, name -> true);
  }

  /**
   * 将任意 Java 对象转换为 Map《String, Object》 支持自定义字段过滤器
   *
   * @param bean 目标对象
   * @param filter 字段名过滤器
   * @return 字段名到字段值的映射
   */
  public static Map<String, Object> toMap(Object bean, FieldFilter filter) {
    if (bean == null) {
      throw new IllegalArgumentException("Input object cannot be null");
    }

    Map<String, Object> result = new HashMap<>();
    Class<?> currentClass = bean.getClass();

    while (currentClass != null) {
      Field[] fields = currentClass.getDeclaredFields();
      for (Field field : fields) {
        field.setAccessible(true);
        try {
          if (filter.test(field.getName())) {
            var o = field.get(bean);
            if (o == null) {
              result.put(field.getName(), null);
            } else if (o instanceof Iterable<?> list) {
              result.put(field.getName(), toMap(list));
            } else if (o instanceof Map<?, ?> map) {
              result.put(field.getName(), map);
            } else if (o instanceof ConstantDesc collection) {
              //   Double,  DynamicConstantDesc, Float, Integer,     Long,   String
              result.put(field.getName(), collection);
            } else {
              result.put(field.getName(), o);
            }
          }
        } catch (IllegalAccessException e) {
          throw new RuntimeException("无法访问字段: " + field.getName(), e);
        }
      }
      currentClass = currentClass.getSuperclass();
    }

    return result;
  }

  /** Convert a list of objects into a list of maps */
  public static List<Map<String, Object>> toMap(Iterable<?> objectList) {
    if (objectList == null) {
      throw new IllegalArgumentException("Input list cannot be null");
    }

    List<Map<String, Object>> resultList = new ArrayList<>();
    for (Object obj : objectList) {
      resultList.add(toMap(obj));
    }
    return resultList;
  }

  /**
   * 拷贝源对象属性到目标对象，默认复制所有字段且不跳过 null 值 支持嵌套对象深拷贝、循环引用检测、字段过滤与 null 跳过
   *
   * @param source 源对象
   * @param target 目标对象
   */
  public static void copyProperties(Object source, Object target) {
    copyProperties(source, target, new HashSet<>(), name -> true, false);
  }

  /**
   * 拷贝属性，支持字段过滤和跳过 null 值字段
   *
   * @param source 源对象
   * @param target 目标对象
   * @param filter 字段过滤器
   * @param skipNull 是否跳过 null 值字段
   */
  public static void copyProperties(
      Object source, Object target, FieldFilter filter, boolean skipNull) {
    copyProperties(source, target, new HashSet<>(), filter, skipNull);
  }

  /**
   * 内部方法：执行字段复制逻辑，支持递归嵌套和循环引用检测
   *
   * @param source 源对象
   * @param target 目标对象
   * @param visited 已访问对象集，防止死循环
   * @param filter 字段过滤器
   * @param skipNull 是否跳过 null 值字段
   */
  private static void copyProperties(
      Object source, Object target, Set<Object> visited, FieldFilter filter, boolean skipNull) {
    if (source == null || target == null) {
      throw new IllegalArgumentException("Source and target cannot be null");
    }

    if (visited.contains(source)) {
      return;
    }
    visited.add(source);

    Class<?> sourceClass = source.getClass();
    Class<?> targetClass = target.getClass();

    while (sourceClass != null) {
      Field[] sourceFields = sourceClass.getDeclaredFields();
      for (Field sourceField : sourceFields) {
        sourceField.setAccessible(true);
        String fieldName = sourceField.getName();

        if (!filter.test(fieldName)) {
          continue;
        }

        try {
          Field targetField;
          try {
            targetField = targetClass.getDeclaredField(fieldName);
            targetField.setAccessible(true);
          } catch (NoSuchFieldException e) {
            continue;
          }

          Object value = sourceField.get(source);
          if (skipNull && value == null) {
            continue;
          }

          if (targetField.getType().isAssignableFrom(sourceField.getType())) {
            targetField.set(target, value);
          } else if (!targetField.getType().isPrimitive()
              && !sourceField.getType().isPrimitive()
              && !targetField.getType().isEnum()
              && !sourceField.getType().isEnum()
              && !targetField.getType().getPackage().getName().startsWith("java")) {

            Object nestedTarget = targetField.get(target);
            if (nestedTarget == null) {
              nestedTarget = targetField.getType().getDeclaredConstructor().newInstance();
              targetField.set(target, nestedTarget);
            }
            copyProperties(value, nestedTarget, visited, filter, skipNull);
          } else {
            System.err.printf(
                "[WARN] 类型不匹配: %s (%s) → %s (%s)%n",
                sourceField.getName(),
                sourceField.getType().getName(),
                targetField.getName(),
                targetField.getType().getName());
          }
        } catch (Exception e) {
          throw new RuntimeException("复制字段失败: " + fieldName, e);
        }
      }
      sourceClass = sourceClass.getSuperclass();
    }
  }
}
